// Memory card support module

#include "burner.h"

static wchar_t szMemoryCardFile[MAX_PATH];

int nMemoryCardStatus = 0;
int nMemoryCardSize;

static int nMinVersion;
static bool bMemCardFC1Format;

static int memCardRead(wchar_t* filename, unsigned char* pData, int size)
{
	const char* szHeader  = "FB1 FC1 ";				// File + chunk identifier
	char szReadHeader[8] = "";

	bMemCardFC1Format = false;

	FILE* file = _wfopen(filename, L"rb");
	if (file == NULL) {
		return 1;
	}

	fread(szReadHeader, 1, 8, file);					// Read identifiers
	if (memcmp(szReadHeader, szHeader, 8) == 0) {

		// FB Alpha memory card file

		int chunkSize = 0;
		int nVersion = 0;

		bMemCardFC1Format = true;

		fread(&chunkSize, 1, 4, file);				// Read chunk size
		if (size < chunkSize - 32) {
			fclose(file);
			return 1;
		}

		fread(&nVersion, 1, 4, file);					// Read version
		if (nVersion < nMinVersion) {
			fclose(file);
			return 1;
		}
		fread(&nVersion, 1, 4, file);

		fseek(file, 0x0C, SEEK_CUR);					// Move file pointer to the start of the data block

		fread(pData, 1, chunkSize - 32, file);		// Read the data
	} else {

		// MAME or old FB Alpha memory card file

		unsigned char* pTemp = new unsigned char[size >> 1];

		memset(pData, 0, size);
		fseek(file, 0x00, SEEK_SET);

		if (pTemp) {
			fread(pTemp, 1, size >> 1, file);

			for (int i = 1; i < size; i += 2) {
				pData[i] = pTemp[i >> 1];
			}

			delete [] pTemp;
		}
	}

	fclose(file);

	return 0;
}

static int memCardWrite(wchar_t* filename, unsigned char* pData, int size)
{
	FILE* file = _wfopen(filename, L"wb");
	if (file == NULL) {
		return 1;
	}

	if (bMemCardFC1Format) {

		// FB Alpha memory card file

		const char* szFileHeader  = "FB1 ";				// File identifier
		const char* szChunkHeader = "FC1 ";				// Chunk identifier
		const int nZero = 0;

		int chunkSize = size + 32;

		fwrite(szFileHeader, 1, 4, file);
		fwrite(szChunkHeader, 1, 4, file);

		fwrite(&chunkSize, 1, 4, file);					// Chunk size

		fwrite(&burnVersion, 1, 4, file);					// Version of FBA this was saved from
		fwrite(&nMinVersion, 1, 4, file);					// Min version of FBA data will work with

		fwrite(&nZero, 1, 4, file);						// Reserved
		fwrite(&nZero, 1, 4, file);						//
		fwrite(&nZero, 1, 4, file);						//

		fwrite(pData, 1, size, file);
	} else {

		// MAME or old FB Alpha memory card file

		unsigned char* pTemp = new unsigned char[size >> 1];
		if (pTemp) {
			for (int i = 1; i < size; i += 2) {
				pTemp[i >> 1] = pData[i];
			}

			fwrite(pTemp, 1, size >> 1, file);

			delete [] pTemp;
		}
	}

	fclose(file);

	return 0;
}

static int __cdecl memCardDoGetSize(struct BurnArea* pba)
{
	nMemoryCardSize = pba->nLen;

	return 0;
}

static int memCardGetSize()
{
	BurnAcb = memCardDoGetSize;
	BurnAreaScan(ACB_MEMCARD, &nMinVersion);

	return 0;
}

int	memCardCreate()
{
	wchar_t szFilter[1024];
	swprintf(szFilter, localiseStringEx(IDS_DISK_FILE_CARD), APP_TITLE);
	memcpy(szFilter + wcslen(szFilter), L" (*.fc)\0*.fc\0\0", 14 * sizeof(wchar_t));

	swprintf (commonChoice, L"memorycard");
	if (commonFileDialog(hScrnWnd, true, localiseStringEx(IDS_MEMCARD_CREATE), szFilter, 0, L"fc")) {
		return 1;
	}
	wcscpy(szMemoryCardFile, commonChoice);

	{
		memCardGetSize();

		unsigned char* pCard = new unsigned char[nMemoryCardSize];
		memset(pCard, 0, nMemoryCardSize);

		bMemCardFC1Format = true;

		if (memCardWrite(szMemoryCardFile, pCard, nMemoryCardSize)) {
			return 1;
		}

		delete [] pCard;
	}

	nMemoryCardStatus = 1;

	return 0;
}

int	memCardSelect()
{
	wchar_t szFilter[1024];
	wchar_t* pszTemp = szFilter;

	pszTemp += swprintf(pszTemp, localiseStringEx(IDS_DISK_ALL_CARD));
	memcpy(pszTemp, L" (*.fc, MEMCARD.\?\?\?)\0*.fc;MEMCARD.\?\?\?\0", 38 * sizeof(wchar_t));
	pszTemp += 38;
	pszTemp += swprintf(pszTemp, localiseStringEx(IDS_DISK_FILE_CARD), APP_TITLE);
	memcpy(pszTemp, L" (*.fc)\0*.fc\0", 13 * sizeof(wchar_t));
	pszTemp += 13;
	pszTemp += swprintf(pszTemp, localiseStringEx(IDS_DISK_FILE_CARD), L"MAME");
	memcpy(pszTemp, L" (MEMCARD.\?\?\?)\0MEMCARD.\?\?\?\0\0", 28 * sizeof(wchar_t));

	if (commonFileDialog(hScrnWnd, false, localiseStringEx(IDS_MEMCARD_SELECT), szFilter, 0, L"fc")) {
		return 1;
	}
	wcscpy(szMemoryCardFile, commonChoice);

	memCardGetSize();

	if (nMemoryCardSize <= 0) {
		return 1;
	}

	nMemoryCardStatus = 1;

	return 0;
}

static int __cdecl memCardDoInsert(struct BurnArea* pba)
{
	if (memCardRead(szMemoryCardFile, (unsigned char*)pba->Data, pba->nLen)) {
		return 1;
	}

	nMemoryCardStatus |= 2;
	menuSync(MENUT_FILE);

	return 0;
}

int	memCardInsert()
{
	if ((nMemoryCardStatus & 1) && (nMemoryCardStatus & 2) == 0) {
		BurnAcb = memCardDoInsert;
		BurnAreaScan(ACB_WRITE | ACB_MEMCARD, &nMinVersion);
	}

	return 0;
}

static int __cdecl memCardDoEject(struct BurnArea* pba)
{
	if (memCardWrite(szMemoryCardFile, (unsigned char*)pba->Data, pba->nLen) == 0) {
		nMemoryCardStatus &= ~2;
		menuSync(MENUT_FILE);

		return 0;
	}

	return 1;
}

int	memCardEject()
{
	if ((nMemoryCardStatus & 1) && (nMemoryCardStatus & 2)) {
		BurnAcb = memCardDoEject;
		nMinVersion = 0;
		BurnAreaScan(ACB_READ | ACB_MEMCARD, &nMinVersion);
	}

	return 0;
}

int	memCardToggle()
{
	if (nMemoryCardStatus & 1) {
		if (nMemoryCardStatus & 2) {
			return memCardEject();
		} else {
			return memCardInsert();
		}
	}

	return 1;
}
